#! /usr/bin/env python
# -*- coding: utf-8 -*-

try:
    import gtk
    import gobject
    from gtk import gdk
except:
    raise SystemExit

import pygtk
if gtk.pygtk_version < (2, 0):
    print "PyGtk 2.0 or later required for this widget"
    raise SystemExit

from math import pi, sqrt
from plane import Plane

class GTKOrtho(gtk.DrawingArea):
    ''' A class for drawing orthographis projection in gtk applocation
    '''
    def __init__(self, density, incr):
       gtk.DrawingArea.__init__(self)
       self.connect("expose_event", self.expose)
       self.isolines_coords = self.isolines_prepare(density, incr)
       self.increment = incr
       self.points = []    
       self.pairs = []
       self.curpair = []

    def expose(self, widget, event):        
        self.context = widget.window.cairo_create()
        
        # set a clip region for the expose event
        self.context.rectangle(event.area.x, event.area.y,
                               event.area.width, event.area.height)
        self.context.clip()
        
        self.width, self.height = event.area.width, event.area.height
        
        self.draw(self.context)
    
    def draw(self, context):
        rect = self.get_allocation()
        x = rect.x + rect.width 
        y = rect.y + rect.height
        
        context.set_source_rgb (1, 1, 1)
        context.set_line_width (0.5)
        context.move_to (0,0)
        context.line_to (x,0)
        context.line_to (x,y)
        context.line_to (0,y)
        context.line_to (0,0)
        context.fill_preserve()
        context.set_source_rgb(0, 0, 0)
        context.stroke()
        
        self.draw_frame(context, self.width, self.height)
        self.draw_isolines(context, self.isolines_coords, self.lx, self.ly, self.increment, self.width, self.height)
        if self.curpair != []:
            self.draw_arrows(context, self.curpair)
        for pair in self.pairs:
            dr,dp=point[0][0],point[0][1]
            x = dr/360*self.width
            y = dp/90*self.height/2
            self.draw_point(context, x, y, [0,0,0]) 
            x = ((dr+180)%360)/360*self.width
            y = self.height - dp/90*self.height/2  
            self.draw_point(context, x, y, [0,0,0])
            dr,dp=point[1][0],point[1][1]
            x = dr/360*self.width
            y = dp/90*self.height/2
            self.draw_point(context, x, y, [0,0,0]) 
            x = ((dr+180)%360)/360*self.width
            y = self.height - dp/90*self.height/2  
            self.draw_point(context, x, y, [0,0,0])
        for point in self.points:
            dr,dp=point[0],point[1]
            x = dr/360*self.width
            y = dp/90*self.height/2
            self.draw_point(context, x, y, [0,0,0]) 
            x = ((dr+180)%360)/360*self.width
            y = self.height - dp/90*self.height/2  
            self.draw_point(context, x, y, [0,0,0])
             
    def draw_frame(self, context, width, height):
        cx, cy = width/2, height/2
        context.set_source_rgb (0, 0, 0)
        context.set_line_width (1)
        context.move_to (cx-width/2.0,cy-height/2.0)
        context.line_to (cx+width/2.0,cy-height/2.0)
        context.line_to (cx+width/2.0,cy+height/2.0)
        context.line_to (cx-width/2.0,cy+height/2.0)
        context.line_to (cx-width/2.0,cy-height/2.0)
        context.stroke()
    
        #draw a horizontal axe  
        context.set_source_rgb (0, 0, 0)
        context.set_line_width (1)
        context.move_to (cx-width/2.0,cy)
        context.line_to (cx+width/2.0,cy)
        context.stroke()        
       
    def isolines_prepare(self, density, increment):
        from grid_plane import PlaneGrid
        from plane import Plane
        import isolines
        
        # prepare grid for doing isolines
        grid = PlaneGrid(incr=[int(increment), int(increment)])
        for pl in density:        
            grid.add(plane=Plane(pl[0], pl[1]), val=pl[2])
        density_grid = grid.returnGrid()
        
        # list of intervals
        intervals= [x/100.0 for x in range(0,100,10)]
        
        # doing isolines
        prepared_matrix=isolines.prepare_matrix(density_grid)    
        calculated_dots=isolines.calculate_dots(prepared_matrix,intervals)
        coords=isolines.calculate_coords(calculated_dots)
        self.lx, self.ly = len(density_grid), len(density_grid[0])
        return coords
    
    def draw_isolines(self, context, coords, lx, ly, increment, width, height):        
        cx, cy = width/2, height/2          
        for cell in coords:        
            for edge in cell:
                # drawing up
                y1,x1 = (edge[1])/(ly)-1, (edge[2]-0.5)/(0.5*lx)-1
                y2,x2 = (edge[3])/(ly)-1, (edge[4]-0.5)/(0.5*lx)-1
                x1_,y1_ = self.recalc_coords(x1, y1, width, height, cx, cy)
                x2_,y2_ = self.recalc_coords(x2, y2, width, height, cx, cy)
                col = [0,0,0]
                self.draw_line(context, x1_, y1_, x2_, y2_, col, linewidth=.5) 
                
                # drawing down
                y1,x1 = 1-(edge[1])/(ly), (((edge[2]-0.5)/(0.5*lx)+1)%2)-1
                y2,x2 = 1-(edge[3])/(ly), (((edge[4]-0.5)/(0.5*lx)+1)%2)-1               
                x1_,y1_ = self.recalc_coords(x1, y1, width, height, cx, cy)
                x2_,y2_ = self.recalc_coords(x2, y2, width, height, cx, cy)
                # skip contours on a edge
                if sqrt((x2_-x1_)**2+(y2_-y1_)**2)>width-5*increment:                
                    continue             
                col = [0,0,0]
                self.draw_line(context, x1_, y1_, x2_, y2_, col, linewidth=.5) 
        
    def draw_line(self, context, x1, y1, x2, y2, col, linewidth=1):               
        context.set_source_rgb(col[0], col[1], col[2])
        context.set_line_width (linewidth)
        context.move_to(x1, y1)
        context.line_to(x2, y2)        
        context.stroke()
    
    def draw_point(self, context, x, y, col, linewidth=1, r=3):
        context.set_source_rgb(col[0], col[1], col[2])
        context.set_line_width (linewidth)
        context.move_to(x + r, y)
        context.arc(x, y, r, 0, 2 * pi)    
        context.fill_preserve()
        context.set_source_rgb(1,1,1)
        context.stroke() 

    def draw_arrows(self, context, planes):
        al = 20 # length of an arrow in degrees
        a = Plane(planes[0][0], 90 - planes[0][1])
        b = Plane(planes[1][0], 90 - planes[1][1])
        c = a.get_perpendicular_between(b).normal()
        ca = c.return_angle_between(a)
        cb = c.return_angle_between(b)
        points=[]
        for angle in range (ca, ca + al, 5):
            s = c.rotated(angle)
            #x, y = self. recalc_coords(s.dir/360, s.dip/90, self.width, self.height, self.width/2, self.height/2)
            x = s.dir/360*self.width
            y = ((90-s.dip)/90)*self.height/2
            points.append([x,y])
        for i in range(len(points)-1):
            self.draw_line(context, points[i][0],points[i][1],points[i+1][0],points[i+1][1], 
                         [.1,.1,.1])
        points=[]
        for angle in range (cb, cb + al, 5):
            s = c.rotated(-1*angle)
            #x, y = self. recalc_coords(s.dir/360, s.dip/90, self.width, self.height, self.width/2, self.height/2)
            x = s.dir/360*self.width
            y = ((90-s.dip)/90)*self.height/2
            points.append([x,y])
        for i in range(len(points)-1):
            self.draw_line(context, points[i][0],points[i][1],points[i+1][0],points[i+1][1], 
                         [.1,.1,.1])

    def recalc_coords(self, x, y, width, height, cx, cy):
        x1 = x*width/2.0 + cx
        y1 = y*height/2.0 + cy
        return x1, y1    
    
    def get_widget_coords(self, x, y):
        if y < self.height/2:
            dp = (1-(self.height/2-y)/(self.height/2))*90
            dr = 360-(self.width-x)/self.width*360
        else:
            dp = (self.height/2-(y-self.height/2))/(self.height/2)*90 
            dr = (360-(self.width-x)/self.width*360+180) % 360
        return dr, dp
   

    def put_a_pole(self, dr, dp):       
        self.points.append([dr,dp])       
        
